/*
 * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
/*
 * Licensed Materials - Property of IBM
 * RMI-IIOP v1.0
 * Copyright IBM Corp. 1998 1999  All Rights Reserved
 *
 */


package com.sun.corba.se.impl.protocol;

import org.omg.PortableServer.Servant;

import org.omg.CORBA.SystemException;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.Any;

import org.omg.CORBA.portable.InvokeHandler;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.OutputStream;
import org.omg.CORBA.portable.UnknownException;
import org.omg.CORBA.portable.ResponseHandler;

import com.sun.org.omg.SendingContext.CodeBase;

import com.sun.corba.se.pept.encoding.OutputObject;
import com.sun.corba.se.pept.protocol.MessageMediator;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.orb.ORBVersion;
import com.sun.corba.se.spi.orb.ORBVersionFactory;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.ObjectKey;
import com.sun.corba.se.spi.ior.ObjectKeyTemplate;
import com.sun.corba.se.spi.ior.ObjectAdapterId;
import com.sun.corba.se.spi.oa.ObjectAdapterFactory;
import com.sun.corba.se.spi.oa.ObjectAdapter;
import com.sun.corba.se.spi.oa.OAInvocationInfo;
import com.sun.corba.se.spi.oa.OADestroyed;
import com.sun.corba.se.spi.oa.NullServant;
import com.sun.corba.se.spi.protocol.CorbaMessageMediator;
import com.sun.corba.se.spi.protocol.CorbaServerRequestDispatcher;
import com.sun.corba.se.spi.protocol.ForwardException;
import com.sun.corba.se.spi.protocol.RequestDispatcherRegistry;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;

import com.sun.corba.se.impl.protocol.SpecialMethod;
import com.sun.corba.se.spi.servicecontext.ServiceContext;
import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.spi.servicecontext.UEInfoServiceContext;
import com.sun.corba.se.spi.servicecontext.CodeSetServiceContext;
import com.sun.corba.se.spi.servicecontext.SendingContextServiceContext;
import com.sun.corba.se.spi.servicecontext.ORBVersionServiceContext;

import com.sun.corba.se.impl.corba.ServerRequestImpl;
import com.sun.corba.se.impl.encoding.MarshalInputStream;
import com.sun.corba.se.impl.encoding.MarshalOutputStream;
import com.sun.corba.se.impl.encoding.CodeSetComponentInfo;
import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry;
import com.sun.corba.se.impl.orbutil.ORBConstants;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.protocol.RequestCanceledException;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.logging.POASystemException;

public class CorbaServerRequestDispatcherImpl
    implements CorbaServerRequestDispatcher {

  protected ORB orb; // my ORB instance
  private ORBUtilSystemException wrapper;
  private POASystemException poaWrapper;

  // Added from last version because it broke the build - RTW
  // XXX remove me and rebuild: probably no longer needed
  // public static final int UNKNOWN_EXCEPTION_INFO_ID = 9;

  public CorbaServerRequestDispatcherImpl(ORB orb) {
    this.orb = orb;
    wrapper = ORBUtilSystemException.get(orb,
        CORBALogDomains.RPC_PROTOCOL);
    poaWrapper = POASystemException.get(orb,
        CORBALogDomains.RPC_PROTOCOL);
  }

  /**
   * XXX/REVISIT:
   * We do not want to look for a servant in the POA/ServantManager case,
   * but we could in most other cases.  The OA could have a method that
   * returns true if the servant MAY exist, and false only if the servant
   * definitely DOES NOT exist.
   *
   * XXX/REVISIT:
   * We may wish to indicate OBJECT_HERE by some mechanism other than
   * returning a null result.
   *
   * Called from ORB.locate when a LocateRequest arrives.
   * Result is not always absolutely correct: may indicate OBJECT_HERE
   * for non-existent objects, which is resolved on invocation.  This
   * "bug" is unavoidable, since in general the object may be destroyed
   * between a locate and a request.  Note that this only checks that
   * the appropriate ObjectAdapter is available, not that the servant
   * actually exists.
   * Need to signal one of OBJECT_HERE, OBJECT_FORWARD, OBJECT_NOT_EXIST.
   *
   * @return Result is null if object is (possibly) implemented here, otherwise an IOR indicating
   * objref to forward the request to.
   * @throws OBJECT_NOT_EXIST is thrown if we know the object does not exist here, and we are not
   * forwarding.
   */
  public IOR locate(ObjectKey okey) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".locate->");
      }

      ObjectKeyTemplate oktemp = okey.getTemplate();

      try {
        checkServerId(okey);
      } catch (ForwardException fex) {
        return fex.getIOR();
      }

      // Called only for its side-effect of throwing appropriate exceptions
      findObjectAdapter(oktemp);

      return null;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".locate<-");
      }
    }
  }

  public void dispatch(MessageMediator messageMediator) {
    CorbaMessageMediator request = (CorbaMessageMediator) messageMediator;
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".dispatch->: " + opAndId(request));
      }

      // to set the codebase information, if any transmitted; and also
      // appropriate ORB Version.
      consumeServiceContexts(request);

      // Now that we have the service contexts processed and the
      // correct ORBVersion set, we must finish initializing the
      // stream.
      ((MarshalInputStream) request.getInputObject())
          .performORBVersionSpecificInit();

      ObjectKey okey = request.getObjectKey();

      // Check that this server is the right server
      try {
        checkServerId(okey);
      } catch (ForwardException fex) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatch: " + opAndId(request)
              + ": bad server id");
        }

        request.getProtocolHandler()
            .createLocationForward(request, fex.getIOR(), null);
        return;
      }

      String operation = request.getOperationName();
      ObjectAdapter objectAdapter = null;

      try {
        byte[] objectId = okey.getId().getId();
        ObjectKeyTemplate oktemp = okey.getTemplate();
        objectAdapter = findObjectAdapter(oktemp);

        java.lang.Object servant = getServantWithPI(request, objectAdapter,
            objectId, oktemp, operation);

        dispatchToServant(servant, request, objectId, objectAdapter);
      } catch (ForwardException ex) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatch: " + opAndId(request)
              + ": ForwardException caught");
        }

        // Thrown by Portable Interceptors from InterceptorInvoker,
        // through Response constructor.
        request.getProtocolHandler()
            .createLocationForward(request, ex.getIOR(), null);
      } catch (OADestroyed ex) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatch: " + opAndId(request)
              + ": OADestroyed exception caught");
        }

        // DO NOT CALL THIS HERE:
        // releaseServant(objectAdapter);
        // The problem is that OADestroyed is only thrown by oa.enter, in
        // which case oa.exit should NOT be called, and neither should
        // the invocationInfo stack be popped.

        // Destroyed POAs can be recreated by normal adapter activation.
        // So just restart the dispatch.
        dispatch(request);
      } catch (RequestCanceledException ex) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatch: " + opAndId(request)
              + ": RequestCanceledException caught");
        }

        // IDLJ generated non-tie based skeletons do not catch the
        // RequestCanceledException. Rethrow the exception, which will
        // cause the worker thread to unwind the dispatch and wait for
        // other requests.
        throw ex;
      } catch (UnknownException ex) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatch: " + opAndId(request)
              + ": UnknownException caught " + ex);
        }

        // RMIC generated tie skeletons convert all Throwable exception
        // types (including RequestCanceledException, ThreadDeath)
        // thrown during reading fragments into UnknownException.
        // If RequestCanceledException was indeed raised,
        // then rethrow it, which will eventually cause the worker
        // thread to unstack the dispatch and wait for other requests.
        if (ex.originalEx instanceof RequestCanceledException) {
          throw (RequestCanceledException) ex.originalEx;
        }

        ServiceContexts contexts = new ServiceContexts(orb);
        UEInfoServiceContext usc = new UEInfoServiceContext(
            ex.originalEx);

        contexts.put(usc);

        SystemException sysex = wrapper.unknownExceptionInDispatch(
            CompletionStatus.COMPLETED_MAYBE, ex);
        request.getProtocolHandler()
            .createSystemExceptionResponse(request, sysex,
                contexts);
      } catch (Throwable ex) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatch: " + opAndId(request)
              + ": other exception " + ex);
        }
        request.getProtocolHandler()
            .handleThrowableDuringServerDispatch(
                request, ex, CompletionStatus.COMPLETED_MAYBE);
      }
      return;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".dispatch<-: " + opAndId(request));
      }
    }
  }

  private void releaseServant(ObjectAdapter objectAdapter) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".releaseServant->");
      }

      if (objectAdapter == null) {
        if (orb.subcontractDebugFlag) {
          dprint(".releaseServant: null object adapter");
        }
        return;
      }

      try {
        objectAdapter.returnServant();
      } finally {
        objectAdapter.exit();
        orb.popInvocationInfo();
      }
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".releaseServant<-");
      }
    }
  }

  // Note that objectAdapter.enter() must be called before getServant.
  private java.lang.Object getServant(ObjectAdapter objectAdapter, byte[] objectId,
      String operation)
      throws OADestroyed {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".getServant->");
      }

      OAInvocationInfo info = objectAdapter.makeInvocationInfo(objectId);
      info.setOperation(operation);
      orb.pushInvocationInfo(info);
      objectAdapter.getInvocationServant(info);
      return info.getServantContainer();
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".getServant<-");
      }
    }
  }

  protected java.lang.Object getServantWithPI(CorbaMessageMediator request,
      ObjectAdapter objectAdapter,
      byte[] objectId, ObjectKeyTemplate oktemp, String operation)
      throws OADestroyed {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".getServantWithPI->");
      }

      // Prepare Portable Interceptors for a new server request
      // and invoke receive_request_service_contexts.  The starting
      // point may throw a SystemException or ForwardException.
      orb.getPIHandler().initializeServerPIInfo(request, objectAdapter,
          objectId, oktemp);
      orb.getPIHandler().invokeServerPIStartingPoint();

      objectAdapter.enter();

      // This must be set just after the enter so that exceptions thrown by
      // enter do not cause
      // the exception reply to pop the thread stack and do an extra oa.exit.
      if (request != null) {
        request.setExecuteReturnServantInResponseConstructor(true);
      }

      java.lang.Object servant = getServant(objectAdapter, objectId,
          operation);

      // Note: we do not know the MDI on a null servant.
      // We only end up in that situation if _non_existent called,
      // so that the following handleNullServant call does not throw an
      // exception.
      String mdi = "unknown";

      if (servant instanceof NullServant) {
        handleNullServant(operation, (NullServant) servant);
      } else {
        mdi = objectAdapter.getInterfaces(servant, objectId)[0];
      }

      orb.getPIHandler().setServerPIInfo(servant, mdi);

      if (((servant != null) &&
          !(servant instanceof org.omg.CORBA.DynamicImplementation) &&
          !(servant instanceof org.omg.PortableServer.DynamicImplementation)) ||
          (SpecialMethod.getSpecialMethod(operation) != null)) {
        orb.getPIHandler().invokeServerPIIntermediatePoint();
      }

      return servant;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".getServantWithPI<-");
      }
    }
  }

  protected void checkServerId(ObjectKey okey) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".checkServerId->");
      }

      ObjectKeyTemplate oktemp = okey.getTemplate();
      int sId = oktemp.getServerId();
      int scid = oktemp.getSubcontractId();

      if (!orb.isLocalServerId(scid, sId)) {
        if (orb.subcontractDebugFlag) {
          dprint(".checkServerId: bad server id");
        }

        orb.handleBadServerId(okey);
      }
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".checkServerId<-");
      }
    }
  }

  private ObjectAdapter findObjectAdapter(ObjectKeyTemplate oktemp) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".findObjectAdapter->");
      }

      RequestDispatcherRegistry scr = orb.getRequestDispatcherRegistry();
      int scid = oktemp.getSubcontractId();
      ObjectAdapterFactory oaf = scr.getObjectAdapterFactory(scid);
      if (oaf == null) {
        if (orb.subcontractDebugFlag) {
          dprint(".findObjectAdapter: failed to find ObjectAdapterFactory");
        }

        throw wrapper.noObjectAdapterFactory();
      }

      ObjectAdapterId oaid = oktemp.getObjectAdapterId();
      ObjectAdapter oa = oaf.find(oaid);

      if (oa == null) {
        if (orb.subcontractDebugFlag) {
          dprint(".findObjectAdapter: failed to find ObjectAdaptor");
        }

        throw wrapper.badAdapterId();
      }

      return oa;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".findObjectAdapter<-");
      }
    }
  }

  /**
   * Always throws OBJECT_NOT_EXIST if operation is not a special method.
   * If operation is _non_existent or _not_existent, this will just
   * return without performing any action, so that _non_existent can return
   * false.  Always throws OBJECT_NOT_EXIST for any other special method.
   * Update for issue 4385.
   */
  protected void handleNullServant(String operation, NullServant nserv) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".handleNullServant->: " + operation);
      }

      SpecialMethod specialMethod =
          SpecialMethod.getSpecialMethod(operation);

      if ((specialMethod == null) ||
          !specialMethod.isNonExistentMethod()) {
        if (orb.subcontractDebugFlag) {
          dprint(".handleNullServant: " + operation
              + ": throwing OBJECT_NOT_EXIST");
        }

        throw nserv.getException();
      }
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".handleNullServant<-: " + operation);
      }
    }
  }

  protected void consumeServiceContexts(CorbaMessageMediator request) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".consumeServiceContexts->: "
            + opAndId(request));
      }

      ServiceContexts ctxts = request.getRequestServiceContexts();
      ServiceContext sc;

      GIOPVersion giopVersion = request.getGIOPVersion();

      // we cannot depend on this since for our local case, we do not send
      // in this service context.  Can we rely on just the CodeSetServiceContext?
      // boolean rtSC = false; // Runtime ServiceContext

      boolean hasCodeSetContext = processCodeSetContext(request, ctxts);

      if (orb.subcontractDebugFlag) {
        dprint(".consumeServiceContexts: " + opAndId(request)
            + ": GIOP version: " + giopVersion);
        dprint(".consumeServiceContexts: " + opAndId(request)
            + ": as code set context? " + hasCodeSetContext);
      }

      sc = ctxts.get(
          SendingContextServiceContext.SERVICE_CONTEXT_ID);

      if (sc != null) {
        SendingContextServiceContext scsc =
            (SendingContextServiceContext) sc;
        IOR ior = scsc.getIOR();

        try {
          ((CorbaConnection) request.getConnection())
              .setCodeBaseIOR(ior);
        } catch (ThreadDeath td) {
          throw td;
        } catch (Throwable t) {
          throw wrapper.badStringifiedIor(t);
        }
      }

      // the RTSC is sent only once during session establishment.  We
      // need to find out if the CodeBaseRef is already set.  If yes,
      // then also the rtSC flag needs to be set to true
      // this is not possible for the LocalCase since there is no
      // IIOPConnection for the LocalCase

      // used for a case where we have JDK 1.3 supporting 1.0 protocol,
      // but sending 2 service contexts, that is not normal as per
      // GIOP rules, based on above information, we figure out that we
      // are talking to the legacy ORB and set the ORB Version Accordingly.

      // this special case tell us that it is legacy SUN orb
      // and not a foreign one
      // rtSC is not available for localcase due to which this generic
      // path would fail if relying on rtSC
      //if (giopVersion.equals(GIOPVersion.V1_0) && hasCodeSetContext && rtSC)
      boolean isForeignORB = false;

      if (giopVersion.equals(GIOPVersion.V1_0) && hasCodeSetContext) {
        if (orb.subcontractDebugFlag) {
          dprint(".consumeServiceCOntexts: " + opAndId(request)
              + ": Determined to be an old Sun ORB");
        }

        orb.setORBVersion(ORBVersionFactory.getOLD());
        // System.out.println("setting legacy ORB version");
      } else {
        // If it didn't include our ORB version service context (below),
        // then it must be a foreign ORB.
        isForeignORB = true;
      }

      // try to get the ORBVersion sent as part of the ServiceContext
      // if any
      sc = ctxts.get(ORBVersionServiceContext.SERVICE_CONTEXT_ID);
      if (sc != null) {
        ORBVersionServiceContext ovsc =
            (ORBVersionServiceContext) sc;

        ORBVersion version = ovsc.getVersion();
        orb.setORBVersion(version);

        isForeignORB = false;
      }

      if (isForeignORB) {
        if (orb.subcontractDebugFlag) {
          dprint(".consumeServiceContexts: " + opAndId(request)
              + ": Determined to be a foreign ORB");
        }

        orb.setORBVersion(ORBVersionFactory.getFOREIGN());
      }
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".consumeServiceContexts<-: " + opAndId(request));
      }
    }
  }

  protected CorbaMessageMediator dispatchToServant(
      java.lang.Object servant,
      CorbaMessageMediator req,
      byte[] objectId, ObjectAdapter objectAdapter) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".dispatchToServant->: " + opAndId(req));
      }

      CorbaMessageMediator response = null;

      String operation = req.getOperationName();

      SpecialMethod method = SpecialMethod.getSpecialMethod(operation);
      if (method != null) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatchToServant: " + opAndId(req)
              + ": Handling special method");
        }

        response = method.invoke(servant, req, objectId, objectAdapter);
        return response;
      }

      // Invoke on the servant using the portable DSI skeleton
      if (servant instanceof org.omg.CORBA.DynamicImplementation) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatchToServant: " + opAndId(req)
              + ": Handling old style DSI type servant");
        }

        org.omg.CORBA.DynamicImplementation dynimpl =
            (org.omg.CORBA.DynamicImplementation) servant;
        ServerRequestImpl sreq = new ServerRequestImpl(req, orb);

        // Note: When/if dynimpl.invoke calls arguments() or
        // set_exception() then intermediate points are run.
        dynimpl.invoke(sreq);

        response = handleDynamicResult(sreq, req);
      } else if (servant instanceof org.omg.PortableServer.DynamicImplementation) {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatchToServant: " + opAndId(req)
              + ": Handling POA DSI type servant");
        }

        org.omg.PortableServer.DynamicImplementation dynimpl =
            (org.omg.PortableServer.DynamicImplementation) servant;
        ServerRequestImpl sreq = new ServerRequestImpl(req, orb);

        // Note: When/if dynimpl.invoke calls arguments() or
        // set_exception() then intermediate points are run.
        dynimpl.invoke(sreq);

        response = handleDynamicResult(sreq, req);
      } else {
        if (orb.subcontractDebugFlag) {
          dprint(".dispatchToServant: " + opAndId(req)
              + ": Handling invoke handler type servant");
        }

        InvokeHandler invhandle = (InvokeHandler) servant;

        OutputStream stream =
            (OutputStream) invhandle._invoke(
                operation,
                (org.omg.CORBA.portable.InputStream) req.getInputObject(),
                req);
        response = (CorbaMessageMediator)
            ((OutputObject) stream).getMessageMediator();
      }

      return response;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".dispatchToServant<-: " + opAndId(req));
      }
    }
  }

  protected CorbaMessageMediator handleDynamicResult(
      ServerRequestImpl sreq,
      CorbaMessageMediator req) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".handleDynamicResult->: " + opAndId(req));
      }

      CorbaMessageMediator response = null;

      // Check if ServerRequestImpl.result() has been called
      Any excany = sreq.checkResultCalled();

      if (excany == null) { // normal return
        if (orb.subcontractDebugFlag) {
          dprint(".handleDynamicResult: " + opAndId(req)
              + ": handling normal result");
        }

        // Marshal out/inout/return parameters into the ReplyMessage
        response = sendingReply(req);
        OutputStream os = (OutputStream) response.getOutputObject();
        sreq.marshalReplyParams(os);
      } else {
        if (orb.subcontractDebugFlag) {
          dprint(".handleDynamicResult: " + opAndId(req)
              + ": handling error");
        }

        response = sendingReply(req, excany);
      }

      return response;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".handleDynamicResult<-: " + opAndId(req));
      }
    }
  }

  protected CorbaMessageMediator sendingReply(CorbaMessageMediator req) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".sendingReply->: " + opAndId(req));
      }

      ServiceContexts scs = new ServiceContexts(orb);
      return req.getProtocolHandler().createResponse(req, scs);
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".sendingReply<-: " + opAndId(req));
      }
    }
  }

  /**
   * Must always be called, just after the servant's method returns.
   * Creates the ReplyMessage header and puts in the transaction context
   * if necessary.
   */
  protected CorbaMessageMediator sendingReply(CorbaMessageMediator req, Any excany) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".sendingReply/Any->: " + opAndId(req));
      }

      ServiceContexts scs = new ServiceContexts(orb);

      // Check if the servant set a SystemException or
      // UserException
      CorbaMessageMediator resp;
      String repId = null;
      try {
        repId = excany.type().id();
      } catch (org.omg.CORBA.TypeCodePackage.BadKind e) {
        throw wrapper.problemWithExceptionTypecode(e);
      }

      if (ORBUtility.isSystemException(repId)) {
        if (orb.subcontractDebugFlag) {
          dprint(".sendingReply/Any: " + opAndId(req)
              + ": handling system exception");
        }

        // Get the exception object from the Any
        InputStream in = excany.create_input_stream();
        SystemException ex = ORBUtility.readSystemException(in);
        // Marshal the exception back
        resp = req.getProtocolHandler()
            .createSystemExceptionResponse(req, ex, scs);
      } else {
        if (orb.subcontractDebugFlag) {
          dprint(".sendingReply/Any: " + opAndId(req)
              + ": handling user exception");
        }

        resp = req.getProtocolHandler()
            .createUserExceptionResponse(req, scs);
        OutputStream os = (OutputStream) resp.getOutputObject();
        excany.write_value(os);
      }

      return resp;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".sendingReply/Any<-: " + opAndId(req));
      }
    }
  }

  /**
   * Handles setting the connection's code sets if required.
   * Returns true if the CodeSetContext was in the request, false
   * otherwise.
   */
  protected boolean processCodeSetContext(
      CorbaMessageMediator request, ServiceContexts contexts) {
    try {
      if (orb.subcontractDebugFlag) {
        dprint(".processCodeSetContext->: " + opAndId(request));
      }

      ServiceContext sc = contexts.get(
          CodeSetServiceContext.SERVICE_CONTEXT_ID);
      if (sc != null) {
        // Somehow a code set service context showed up in the local case.
        if (request.getConnection() == null) {
          return true;
        }

        // If it's GIOP 1.0, it shouldn't have this context at all.  Our legacy
        // ORBs sent it and we need to know if it's here to make ORB versioning
        // decisions, but we don't use the contents.
        if (request.getGIOPVersion().equals(GIOPVersion.V1_0)) {
          return true;
        }

        CodeSetServiceContext cssc = (CodeSetServiceContext) sc;
        CodeSetComponentInfo.CodeSetContext csctx = cssc.getCodeSetContext();

        // Note on threading:
        //
        // getCodeSetContext and setCodeSetContext are synchronized
        // on the Connection.  At worst, this will result in
        // multiple threads entering this block and calling
        // setCodeSetContext but not actually changing the
        // values on the Connection.
        //
        // Alternative would be to lock the connection for the
        // whole block, but it's fine either way.

        // The connection's codeSetContext is null until we've received a
        // request with a code set context with the negotiated code sets.
        if (((CorbaConnection) request.getConnection())
            .getCodeSetContext() == null) {

          // Use these code sets on this connection
          if (orb.subcontractDebugFlag) {
            dprint(".processCodeSetContext: " + opAndId(request)
                + ": Setting code sets to: " + csctx);
          }

          ((CorbaConnection) request.getConnection())
              .setCodeSetContext(csctx);

          // We had to read the method name using ISO 8859-1
          // (which is the default in the CDRInputStream for
          // char data), but now we may have a new char
          // code set.  If it isn't ISO8859-1, we must tell
          // the CDR stream to null any converter references
          // it has created so that it will reacquire
          // the code sets again using the new info.
          //
          // This should probably compare with the stream's
          // char code set rather than assuming it's ISO8859-1.
          // (However, the operation name is almost certainly
          // ISO8859-1 or ASCII.)
          if (csctx.getCharCodeSet() !=
              OSFCodeSetRegistry.ISO_8859_1.getNumber()) {
            ((MarshalInputStream) request.getInputObject())
                .resetCodeSetConverters();
          }
        }
      }

      // If no code set information is ever sent from the client,
      // the server will use ISO8859-1 for char and throw an
      // exception for any wchar transmissions.
      //
      // In the local case, we use ORB provided streams for
      // marshaling and unmarshaling.  Currently, they use
      // ISO8859-1 for char/string and UTF16 for wchar/wstring.
      return sc != null;
    } finally {
      if (orb.subcontractDebugFlag) {
        dprint(".processCodeSetContext<-: " + opAndId(request));
      }
    }
  }

  protected void dprint(String msg) {
    ORBUtility.dprint("CorbaServerRequestDispatcherImpl", msg);
  }

  protected String opAndId(CorbaMessageMediator mediator) {
    return ORBUtility.operationNameAndRequestId(mediator);
  }
}

// End of file.
